package com.aizuda.encrypt.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.aizuda.encrypt.constant.EncryptConstant;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.select.SetOperationList;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

/**
 * sql解析工具类
 *
 * @author nn200433
 * @date 2021-09-04 05:14:04
 */
@Slf4j
public class ParseSQLUtil {

    /**
     * 获得真正的处理对象,可能多层代理.
     */
    public static Object getRealTarget(Object target) {
        if (Proxy.isProxyClass(target.getClass())) {
            return getRealTarget(SystemMetaObject.forObject(target).getValue(EncryptConstant.TARGET_KEY));
        }
        return target;
    }

    /**
     * 获取查询列表
     *
     * @param selectBody 查询
     */
    public static List<PlainSelect> getPlainSelectList(SelectBody selectBody) {
        List<PlainSelect> plainSelectList = new ArrayList<PlainSelect>();
        if (null != selectBody) {
            if (selectBody.getClass().equals(SetOperationList.class)) {
                // 判断sql中包含union的情况
                plainSelectList = ((SetOperationList) selectBody).getSelects().stream()
                        .map(s -> (PlainSelect) s)
                        .collect(Collectors.toList());
            } else if (selectBody.getClass().equals(PlainSelect.class)) {
                plainSelectList.add((PlainSelect) selectBody);
            }
        }
        return plainSelectList;
    }

    /**
     * 获取新属性名
     *
     * @param property 财产
     * @return {@link String }
     * @author nn200433
     */
    public static String getNewProperty(String property) {
        return EncryptConstant.PARAMETER_OBJECT_KEY + StrUtil.DOT + property;
    }

    /**
     * 获取新值
     *
     * @param sql            sql
     * @param originalValues 原始值
     * @return {@link String }
     * @author nn200433
     */
    public static String getNewValue(String sql, final String originalValues,
                                     BiFunction<String, String, String> replaceFun) {
        String  resultValue       = originalValues;
        String  newOriginalValues = originalValues;
        boolean isLike            = false;
        if (StrUtil.containsIgnoreCase(sql, "like")) {
            isLike = true;
            // 处理 like 值
            if (StrUtil.startWith(originalValues, "%")) {
                newOriginalValues = newOriginalValues.substring(1);
            }
            if (StrUtil.endWith(originalValues, "%")) {
                newOriginalValues = newOriginalValues.substring(0, newOriginalValues.length() - 1);
            }
        }
        if (null != replaceFun) {
            if (isLike) {
                resultValue = "%" + replaceFun.apply(sql, newOriginalValues) + "%";
            } else {
                resultValue = StrUtil.replace(resultValue, newOriginalValues, replaceFun.apply(sql, newOriginalValues));
            }
        }
        return resultValue;
    }

    /**
     * 反射取值
     *
     * @param obj      对象
     * @param property 属性
     * @return {@link Object }
     * @author nn200433
     */
    public static Object getValue(Object obj, String property) {
        if (ObjectUtil.isNull(obj)) {
            return StrUtil.EMPTY;
        }
        if (StrUtil.isBlank(property)) {
            return obj;
        }
        List<String> keyList = StrUtil.split(property, StrUtil.DOT);
        String       key     = keyList.get(0);
        keyList.remove(0);
        property = CollUtil.join(keyList, StrUtil.DOT);

        if (obj instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) obj;
            return getValue(map.get(key), property);
        } else if (obj instanceof QueryWrapper) {
            QueryWrapper        wrapper             = (QueryWrapper) obj;
            Map<String, Object> paramNameValuePairs = wrapper.getParamNameValuePairs();
            return getValue(paramNameValuePairs.get(key), property);
        } else {
            Object invoke = ReflectUtil.invoke(obj, "get" + StrUtil.upperFirst(key));
            return getValue(invoke, property);
        }
    }

}